package com.yc.mvc;

import com.yc.mvc.util.Utils;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.*;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Enumeration;
import java.util.Map;

/**
 * Company 源辰信息
 *
 * @author navy
 * @date 2024/8/10
 * Email haijunzhou@hnit.edu.cn
 */
public class MyDispatcherServlet extends HttpServlet {

    WebApplicationContext context;

    public MyDispatcherServlet(WebApplicationContext context) {
        this.context = context;
    }

//    @Override
//    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        resp.getWriter().println("hello world!");
//    }


    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 查找控制器方法
        Handler handler = handlerMapping(req);

        Object ret = "测试路径解析";

        if(handler == null){
            // TODO 静态资源处理
            int status = defaultServletHandling(req, resp);
            if(status == 404){
                // TODO 返回 404 页面
            } else {
                return;
            }
        } else {
            // 执行控制器方法
            ret = handler.handle(req,resp);
        }

        // 响应结果
        Controller controller = handler.getContorller().getClass().getAnnotation(Controller.class);
//        RestController restController = handler.getContorller().getClass().getAnnotation(RestController.class);

        if(controller != null){
            ResponseBody responseBody = handler.getMethod().getAnnotation(ResponseBody.class);
            if(responseBody == null){
                // 执行页面跳转
                String viewName;
                if(ret instanceof String){
                    // 字符串视图名:  forward:... or redirect:...
                    viewName = ret.toString();
                } else if(ret instanceof ModelAndView){
                    viewName = ((ModelAndView) ret).getViewName();
                } else {
                    throw new RuntimeException("返回结果类型错误!");
                }
                if(viewName.startsWith("forward:")){
                    throw new RuntimeException("请求转发未实现!");
                }
                viewName = viewName.substring("redirect:".length());
                // 响应重定向
                resp.sendRedirect(viewName);
                return;
            }
        } else {
            if(ret instanceof ModelAndView){
                String viewName = ((ModelAndView) ret).getViewName();
                viewName = viewName.substring("redirect:".length());
                // 响应重定向
                resp.sendRedirect(viewName);
                return;
            }
        }

        resp.setContentType("application/json;charset=utf-8");
        resp.getWriter().println(ret);

    }

    // 处理静态资源
    private int defaultServletHandling(HttpServletRequest req, HttpServletResponse resp) {
        // 获取静态资源的路径
        // http://localhost:8080/spring_mvc_war_exploded/index.html
        String path = req.getServletPath();
        String uri = req.getRequestURI();
        // /index.html
        System.out.println("path = " + path);
        // /spring_mvc_war_exploded/body2
        System.out.println("uri = " + uri);
        // 获取资源磁盘轮径
        String realPath = this.getServletContext().getRealPath(path);
        System.out.println("realPath = " + realPath);

        File file = new File(realPath);
        if(file.exists()==false){
            return 404;
        }

        if (file.getName().endsWith(".html")) {
            resp.setContentType("text/html;charset=utf-8");
        } else if (file.getName().endsWith(".css")) {
            resp.setContentType("text/css;charset=utf-8");
        } if (file.getName().endsWith(".js")) {
            resp.setContentType("applicaiton/javascirpt;charset=utf-8");
        }

        // 读取静态资源
        try(FileInputStream fis = new FileInputStream(realPath)){
            ServletOutputStream out = resp.getOutputStream();
            byte[] bytes = new byte[1024];
            int count;
            while((count = fis.read(bytes))>-1){
                // 返回浏览器
                out.write(bytes,0,count);
            }
        }catch (Exception e){
            throw new RuntimeException("读取文件错误!",e);
        }
        return 200;
    }

    private Handler handlerMapping(HttpServletRequest req) {
        Handler ret = null;
        Map<String, Object> controllerMap = context.getBeansWithAnnotation(Controller.class);
        Map<String, Object> restControllerMap = context.getBeansWithAnnotation(RestController.class);
        System.out.println("controllerMap = " + controllerMap);
        System.out.println("restControllerMap = " + restControllerMap);
        controllerMap.putAll(restControllerMap);
        // 根据地址查找控制器方法
        String uri = req.getRequestURI();  // hello?name=xxx   "hello"
        System.out.println("uri = " + uri);
        for (Object contorller : controllerMap.values()) {
            RequestMapping classRequestMapping = contorller.getClass().getAnnotation(RequestMapping.class);
            for (Method declaredMethod : contorller.getClass().getDeclaredMethods()) {
                RequestMapping methodRequestMapping = declaredMethod.getAnnotation(RequestMapping.class);
                String addr;

                if(methodRequestMapping == null) {
                    System.out.println("当前方法是用的 GetMapping PostMapping ...");
                    continue;
                }

                if(methodRequestMapping.value().length>0){
                    addr = methodRequestMapping.value()[0];
                } else {
                    addr = methodRequestMapping.path()[0];
                }
                if(classRequestMapping != null){
                    if(classRequestMapping.value().length>0){
                        addr = classRequestMapping.value()[0] + "/" + addr;
                    } else {
                        addr = classRequestMapping.path()[0] + "/" + addr;
                    }
                }
                // 8080/xxxxx/a.html   => contextPath => /xxxxxx
                String contextPath = this.getServletContext().getContextPath();
                addr = contextPath + (addr.startsWith("/")?"":"/") + addr;
                System.out.println("addr = " + addr);
                if(addr.contains("{")){
                    // TODO 路径参数
                } else if(addr.contains("?") || addr.contains("*")){
                    // TODO 模糊匹配 => 正则表达式
                } else {
                    if(uri.equals(addr)){
                        // 构建 handler
                        ret = new Handler(contorller, declaredMethod);
                    }
                }
            }
        }
        return ret;
    }

    // 生命周期方法
    @Override
    public void init() throws ServletException {
        System.out.println("手动控制加载 bean ...");
        ((AnnotationConfigWebApplicationContext) this.context).refresh();
        super.init();
    }
}

@Data
@AllArgsConstructor
class Handler{

    Object contorller;
    Method method;

    public Object handle(HttpServletRequest req, HttpServletResponse resp) {
        Object ret = null;
        method.setAccessible(true);
        // 获取方法参数 => 获取方法参数名
        if (method.getParameterCount() == 0) {
            // 无方法参数
            try {
                ret = method.invoke(contorller);
            } catch (Exception e) {
                throw new RuntimeException("执行控制器方法错误!", e);
            }
        } else {
            // 有方法参数
            // 获取方法参数名数组
            Parameter[] parameters = method.getParameters();
            String[] parameterNames = Utils.getParameterNames(method);
            Object[] parameterValues = new Object[parameterNames.length];
            for (int i = 0; i < parameterNames.length; i++) {
                String parameterName = parameterNames[i];
                String svalue = req.getParameter(parameterName);
                Object ovalue = null;
//                parameters[i].getClass(); // 错误: Parameter.class
                Class<?> type = parameters[i].getType();// 正确: String.class
                if (String.class.isAssignableFrom(type)) {
                    ovalue = svalue;
                } else if (int.class.isAssignableFrom(type) || Integer.class.isAssignableFrom(type)) {
                    ovalue = Integer.valueOf(svalue);
                } else if (HttpServletRequest.class.isAssignableFrom(type)) {
                    ovalue = req;
                } else if (HttpServletResponse.class.isAssignableFrom(type)) {
                    ovalue = resp;
                } else if (InputStream.class.isAssignableFrom(type)) {
                    try {
                        ovalue = req.getInputStream();
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                } else if (OutputStream.class.isAssignableFrom(type)) {
//                    try {
//                        ovalue = resp.getOutputStream();
//                    } catch (IOException e) {
//                        throw new RuntimeException(e);
//                    }
                } else if (HttpSession.class.isAssignableFrom(type)) {
                    ovalue = req.getSession();
                } else if (Person.class.isAssignableFrom(type)) {
                    // 自动装箱
                    try {
                        Object person = type.newInstance();
                        // 获取每个参数舍之到 person
                        Enumeration<String> names = req.getParameterNames();
                        while (names.hasMoreElements()) {
                            String name = names.nextElement();
                            System.out.println("name = " + name);
                            String value = req.getParameter(name);
                            System.out.printf("===== %s = %s \n", name, value);
                            // TODO 自动装箱
                            Utils.setValue(person, name, value);
                        }
                        ovalue = person;
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                } else if(ModelAndView.class.isAssignableFrom(type)){
                    ovalue = new ModelAndView();
                } else {
                    // TODO 其他类型待扩展  Object ModelAndView ...
                }
                parameterValues[i] = ovalue;
            }
            try {
                ret = method.invoke(contorller,parameterValues);
            } catch (Exception e) {
                throw new RuntimeException("执行控制器方法错误",e);
            }
        }

        return ret;
    }
}
